%%%-------------------------------------------------------------------
%%% @author Filippo Pacini <pacini@sgconsulting.it>
%%% @copyright 2008 S.G. Consulting s.r.l.
%%%
%%% License :
%%% The contents of this file are subject to the Mozilla Public
%%% License Version 1.1 (the "License"); you may not use this file
%%% except in compliance with the License. You may obtain a copy of
%%% the License at http://www.mozilla.org/MPL/
%%%
%%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and
%%% limitations under the License.
%%% The Initial Developer of the Original Code is S.G. Consulting
%%% srl. Portions created by S.G. Consulting s.r.l. are Copyright (C)
%%% 2008 S.G. Consulting srl. All Rights Reserved.
%%%
%%% @doc 
%%% <p>ewgi middleware module. Iplements a simple dispatcher.</p>
%%% <p>Matches the requested url to the appropriate handler.
%%% The request is matched using url and method. Regexps ca be used 
%%% in place for urls. Method is matched with atoms 'GET' 'POST', ...
%%% or with the don't care '_' to match all possible methods.</p>
%%%
%%% @end
%%%
%%%-------------------------------------------------------------------
-module(ewgi_dispatcher, [MatchSpec]).

-include_lib("ewgi/include/ewgi.hrl").

%% API
-export([get_handler/1]).

get_handler(Ctx) ->
    Meth = ewgi_api:method(Ctx),
    Url  = ewgi_api:path_info(Ctx),
    {Status, Body} = case match_req(MatchSpec, Meth, Url) of
                         {ok, {Mod, F}}       -> Mod:F(Ctx);
                         {ok, {Node, Mod, F}} -> rpc:call(Node, Mod, F, [Ctx]);
                         {error, not_found} -> 
                             {?NOT_FOUND, ["Page not found!"]};
                         {error, method_not_allowed} ->
                             {?METHOD_NOT_ALLOWED, ["Method not allowed!"]};
                         _ -> 
                             {?INTERNAL_SERVER_ERROR, ["Internal server error!"]}
                     end,
    ewgi_api:set_response(#ewgi_response{status=Status, headers=[], message_body=Body}, Ctx).
 
%%====================================================================
%% Internal functions
%%====================================================================
%%--------------------------------------------------------------------
%% @spec match_req(MatchSpec, Method::atom(), Url::string()) -> {ok, Handler}|{error, not_found}
%% 
%% @doc Tryes to match the request.
%% @end
%%--------------------------------------------------------------------
match_req(MatchSpec, Method, Url) ->
    match_url(MatchSpec, Method, Url).

%%--------------------------------------------------------------------
%% @spec match_url(MatchSpec, Method::atom(), Url::string()) -> 
%%                                         {ok, Handler} |
%%                                         {error, not_found} |
%%                                         {error, method_not_allowed}
%% 
%% @doc Tryes to match the url.
%% @end
%%--------------------------------------------------------------------
match_url([{MethSpec, Url, Handler}|Rest], Method, Url) ->
    case match_method(MethSpec, Method) of
        true ->
            {ok, Handler};
        _ ->
            case match_url(Rest, Method, Url) of
                {ok, H} -> {ok, H};
                {error, not_found} -> {error, method_not_allowed}
            end
    end;
match_url([{MethSpec, {regexp, UrlRe}, Handler}|Rest], Method, Url) ->
    case regexp:match(Url, UrlRe) of
	{match, _Start, _End} ->
            case match_method(MethSpec, Method) of
                true -> {ok, Handler};
                _ ->
                    case match_url(Rest, Method, Url) of
                        {ok, H} -> {ok, H};
                        {error, not_found} -> {error, method_not_allowed}
                    end
            end;
	_ ->
	    match_url(Rest, Method, Url)
    end;
match_url([_H|Rest], Method, Url) ->
    match_url(Rest, Method, Url);
match_url([], _Method, _Url) ->
    {error, not_found}.


%%--------------------------------------------------------------------
%% @spec match_method(MethSpec::atom(), Method::atom()) -> true | false
%% 
%% @doc Tryes to match the method.
%% @end
%%--------------------------------------------------------------------
match_method('_', _) ->
    true;
match_method(Method, Method) ->
    true;
match_method(_Meth1, _Meth2) ->
    false.
